package value

import (
	"context"
	"strconv"
)

type Number interface {
	NixValue

	Add(Number) Number
	Sub(Number) Number
	Mul(Number) Number
	Div(Number) Number

	ToFloat() Float
}

type Int int64

func (i Int) NixType() string {
	return "int"
}

func (i Int) String() string {
	return strconv.FormatInt(int64(i), 10)
}

func (Int) Strict(context.Context) error {
	return nil
}

func (lhs Int) Add(other Number) Number {
	switch rhs := other.(type) {
	case Int:
		return lhs + rhs
	default:
		return Float(lhs) + rhs.ToFloat()
	}
}

func (lhs Int) Sub(other Number) Number {
	switch rhs := other.(type) {
	case Int:
		return lhs - rhs
	default:
		return Float(lhs) - rhs.ToFloat()
	}
}

func (lhs Int) Mul(other Number) Number {
	switch rhs := other.(type) {
	case Int:
		return lhs * rhs
	default:
		return Float(lhs) * rhs.ToFloat()
	}
}

func (lhs Int) Div(other Number) Number {
	switch rhs := other.(type) {
	case Int:
		return lhs / rhs
	default:
		return Float(lhs) / rhs.ToFloat()
	}
}

func (n Int) ToFloat() Float {
	return Float(n)
}

type Float float64

func (f Float) NixType() string {
	return "float"
}

func (f Float) String() string {
	return strconv.FormatFloat(float64(f), 'g', -1, 64)
}

func (Float) Strict(context.Context) error {
	return nil
}

func (lhs Float) Add(rhs Number) Number {
	return lhs + rhs.ToFloat()
}

func (lhs Float) Sub(rhs Number) Number {
	return lhs - rhs.ToFloat()
}

func (lhs Float) Mul(rhs Number) Number {
	return lhs * rhs.ToFloat()
}

func (lhs Float) Div(rhs Number) Number {
	return lhs / rhs.ToFloat()
}

func (n Float) ToFloat() Float {
	return n
}
